Module to manage date and time
!! Module to manage date and time !|author: <a href="mailto:giovanni.ravazzani@polimi.it">Giovanni Ravazzani</a> ! license: <a href="http://www.gnu.org/licenses/">GPL</a> ! !### History ! ! current version 1.3 - 31st May 2023 ! ! | version | date | comment | ! |----------|-------------|----------| ! | 1.0 | 09/Nov/2008 | Original code | ! | 1.1 | 12/Jun/2011 | Add function to DateTimeIsDefault | ! | 1.2 | 30/Mar/2018 | date transformed to UTC in TimeDifference before computing difference | ! | 1.3 | 31/May/2023 | new function GetDayOfWeek | ! ! !### License ! license: GNU GPL <http://www.gnu.org/licenses/> ! ! This file is part of ! ! MOSAICO -- MOdular library for raSter bAsed hydrologIcal appliCatiOn. ! ! Copyright (C) 2011 Giovanni Ravazzani ! !### Code Description ! Language: Fortran 90. ! ! Software Standards: "European Standards for Writing and ! Documenting Exchangeable Fortran 90 Code". ! !### Module Description: ! set of fortran routines to manage date and time. ! The module adhers to the International Standard ISO 8601 specifications. ! Date and time is expressed in the form `YYYY-MM-DDThh:mm:ssTZD` ! where: ! ! `YYYY` = four-digit year ! ! `MM` = two-digit month (01=January, etc.) ! ! `DD` = two-digit day of month (01 through 31) ! ! `hh` = two digits of hour (00 through 23) (am/pm NOT allowed) ! ! `mm` = two digits of minute (00 through 59) ! ! `ss` = two digits of second (00 through 59) ! ! `TZD` = time zone designator (+hh:mm or -hh:mm) ! ! A time zone offset of "+hh:mm" indicates that the date/time uses a local ! time zone which is `hh` hours and `mm` minutes ahead of UTC ! (Coordinated Universal Time). A time zone offset of "-hh:mm" ! indicates that the date/time uses a local time zone which is ! `hh` hours and `mm` minutes behind UTC. ! ! Example: `2007-03-05T01:00:00+02:00` ! ! This standard notation helps to avoid confusion in international ! communication caused by the many different national notations and ! increases the portability of computer user interfaces. ! In addition, these formats have several important advantages for ! computer usage compared to other traditional date and time notations. ! ! References: ! http://www.w3.org/TR/NOTE-datetime ! ! http://en.wikipedia.org/wiki/ISO_8601 ! ! http://www.probabilityof.com/iso/8601v2000.pdf ! MODULE Chronos ! ! Modules used: ! USE DataTypeSizes ,ONLY: & ! Imported Parameters: short,long,float,double USE LogLib, ONLY : & ! Imported Routines: Catch USE ErrorCodes, ONLY : & ! Imported parameters: DateTimeError, unknownOption USE StringManipulation, ONLY : & !imported routines: StringToLong, ToString ! Declarations must be of the form: ! [type] [VariableName] ! Description/ purpose of variable IMPLICIT NONE ! Global (i.e. public) Declarations: ! Global Procedures: PUBLIC :: IsLeapYear PUBLIC :: DayOfYear PUBLIC :: AddSeconds PUBLIC :: AddMinutes PUBLIC :: AddHours PUBLIC :: AddDays PUBLIC :: IsUTC PUBLIC :: ToUTC PUBLIC :: Now PUBLIC :: UtcNow PUBLIC :: GetYear PUBLIC :: GetMonth PUBLIC :: GetDay PUBLIC :: GetDayOfWeek PUBLIC :: GetHour PUBLIC :: GetMinute PUBLIC :: GetSecond PUBLIC :: GetTimeZone PUBLIC :: SetYear PUBLIC :: SetMonth PUBLIC :: SetDay PUBLIC :: SetHour PUBLIC :: SetMinute PUBLIC :: SetSecond PUBLIC :: SetTimeZone PUBLIC :: DateTimeIsDefault ! Global Type Definitions: TYPE:: DateTime INTEGER (KIND = short) :: year INTEGER (KIND = short) :: month INTEGER (KIND = short) :: day INTEGER (KIND = short) :: hour INTEGER (KIND = short) :: minute INTEGER (KIND = short) :: second !INTEGER (KIND = short) :: millisecond INTEGER (KIND = short) :: TZhour INTEGER (KIND = short) :: TZminute CHARACTER (LEN = 1) :: TZsign END TYPE DateTime ! Global Parameters: INTEGER, PARAMETER :: timeStringLength = 25 CHARACTER (LEN = timeStringLength) :: timeString CHARACTER (LEN = timeStringLength), PARAMETER :: timeDefault = '0000-00-00T00:00:00+00:00' ! Global Scalars: ! Global Arrays: ! Local (i.e. private) Declarations: ! Local Procedures: PRIVATE :: Equality PRIVATE :: GreaterThan PRIVATE :: GreaterThanOrEqual PRIVATE :: LessThan PRIVATE :: LessThanOrEqual PRIVATE :: Inequality PRIVATE :: Parse PRIVATE :: TimeToString PRIVATE :: DateCheck PRIVATE :: DaysInMonth PRIVATE :: Copy PRIVATE :: SecondOfYear PRIVATE :: SecondsToEnd PRIVATE :: TimeDifference ! Local Type Definitions: ! Local Parameters: ! Local Scalars: ! Local Arrays: ! Operator definitions: ! Define new operators or overload existing ones. INTERFACE ASSIGNMENT( = ) MODULE PROCEDURE TimeToString MODULE PROCEDURE Parse MODULE PROCEDURE Copy END INTERFACE INTERFACE OPERATOR ( == ) MODULE PROCEDURE Equality END INTERFACE INTERFACE OPERATOR ( + ) MODULE PROCEDURE AddSeconds END INTERFACE INTERFACE OPERATOR ( - ) MODULE PROCEDURE TimeDifference END INTERFACE INTERFACE OPERATOR ( > ) MODULE PROCEDURE GreaterThan END INTERFACE INTERFACE OPERATOR ( >= ) MODULE PROCEDURE GreaterThanOrEqual END INTERFACE INTERFACE OPERATOR ( < ) MODULE PROCEDURE LessThan END INTERFACE INTERFACE OPERATOR ( <= ) MODULE PROCEDURE LessThanOrEqual END INTERFACE INTERFACE OPERATOR ( /= ) MODULE PROCEDURE Inequality END INTERFACE !======= CONTAINS !======= ! Define procedures contained in this module. !============================================================================== !| Description: ! return `true` if time1 is equal to time2. Before comparison, dates are ! converted to UTC FUNCTION Equality & ! (time1, time2) & ! RESULT (isEqual) IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time1, time2 ! Local declarations: LOGICAL :: isEqual TYPE (DateTime) :: tempTime1, tempTime2 !------------end of declaration------------------------------------------------ !converto to utc tempTime1 = ToUtc (time1) tempTime2 = ToUtc (time2) !perform comparison IF( tempTime1 % year == tempTime2 % year .AND. & tempTime1 % month == tempTime2 % month .AND. & tempTime1 % day == tempTime2 % day .AND. & tempTime1 % hour == tempTime2 % hour .AND. & tempTime1 % minute == tempTime2 % minute .AND. & tempTime1 % second == tempTime2 % second ) THEN isEqual = .TRUE. ELSE isEqual = .FALSE. END IF END FUNCTION Equality !============================================================================== !| Description: ! return `true` if time1 is greater than time2 FUNCTION GreaterThan & ! (time1, time2) & ! RESULT (greater) IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time1, time2 ! Local declarations: LOGICAL :: greater TYPE (DateTime) :: tempTime1, tempTime2 !------------end of declaration------------------------------------------------ !converto to utc tempTime1 = ToUtc (time1) tempTime2 = ToUtc (time2) !perform comparison IF(tempTime1 % year > tempTime2 % year) THEN greater = .TRUE. ELSE IF(tempTime1 % year < tempTime2 % year) THEN greater = .FALSE. ELSE IF(tempTime1 % month > tempTime2 % month) THEN greater = .TRUE. ELSE IF(tempTime1 % month < tempTime2 % month) THEN greater = .FALSE. ELSE IF(tempTime1 % day > tempTime2 % day) THEN greater = .TRUE. ELSE IF(tempTime1 % day < tempTime2 % day) THEN greater = .FALSE. ELSE IF(tempTime1 % hour > tempTime2 % hour) THEN greater = .TRUE. ELSE IF(tempTime1 % hour < tempTime2 % hour) THEN greater = .FALSE. ELSE IF(tempTime1 % minute > tempTime2 % minute) THEN greater = .TRUE. ELSE IF(tempTime1 % minute < tempTime2 % minute) THEN greater = .FALSE. ELSE IF(tempTime1 % second > tempTime2 % second) THEN greater = .TRUE. ELSE greater = .FALSE. END IF END FUNCTION GreaterThan !============================================================================== !| Description: ! return `true` if time1 is greater than time2 ! or time1 is equal to time2 FUNCTION GreaterThanOrEqual & ! (time1, time2) & ! RESULT (greatequal) IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time1, time2 ! Local scalars: LOGICAL :: greatequal TYPE (DateTime) :: tempTime1, tempTime2 !------------end of declaration------------------------------------------------ !converto to utc tempTime1 = ToUtc (time1) tempTime2 = ToUtc (time2) IF ( tempTime1 > tempTime2 .OR. tempTime1 == tempTime2 ) THEN greatequal = .TRUE. ELSE greatequal = .FALSE. END IF END FUNCTION GreaterThanOrEqual !============================================================================== !| Description: ! return `true` if time1 is less than time2 FUNCTION LessThan & ! (time1, time2) & ! RESULT (less) IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time1, time2 ! Local declarations: LOGICAL :: less TYPE (DateTime) :: tempTime1, tempTime2 !------------end of declaration------------------------------------------------ !converto to utc tempTime1 = ToUtc (time1) tempTime2 = ToUtc (time2) IF(tempTime1 % year < tempTime2 % year) THEN less = .TRUE. ELSE IF(tempTime1 % year > tempTime2 % year) THEN less = .FALSE. ELSE IF(tempTime1 % month < tempTime2 % month) THEN less = .TRUE. ELSE IF(tempTime1 % month > tempTime2 % month) THEN less = .FALSE. ELSE IF(tempTime1 % day < tempTime2 % day) THEN less = .TRUE. ELSE IF(tempTime1 % day > tempTime2 % day) THEN less = .FALSE. ELSE IF(tempTime1 % hour < tempTime2 % hour) THEN less = .TRUE. ELSE IF(tempTime1 % hour > tempTime2 % hour) THEN less = .FALSE. ELSE IF(tempTime1 % minute < tempTime2 % minute) THEN less = .TRUE. ELSE IF(tempTime1 % minute > tempTime2 % minute) THEN less = .FALSE. ELSE IF(tempTime1 % second < tempTime2 % second) THEN less = .TRUE. ELSE less = .FALSE. END IF END FUNCTION LessThan !============================================================================== !| Description: ! return `true` if time1 is less than time2 ! or time1 is equal to time2 FUNCTION LessThanOrEqual & ! (time1, time2) & ! RESULT (lessequal) IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time1, time2 ! Local declarations: LOGICAL :: lessequal TYPE (DateTime) :: tempTime1, tempTime2 !------------end of declaration------------------------------------------------ !converto to utc tempTime1 = ToUtc (time1) tempTime2 = ToUtc (time2) IF ( tempTime1 < tempTime2 .OR. tempTime1 == tempTime2 ) THEN lessequal = .TRUE. ELSE lessequal = .FALSE. END IF END FUNCTION LessThanOrEqual !============================================================================== !| Description: ! return `true` if time1 is different from time2 FUNCTION Inequality & ! (time1, time2) & ! RESULT (isInequal) IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time1, time2 ! Local declarations: LOGICAL :: isInequal TYPE (DateTime) :: tempTime1, tempTime2 !------------end of declaration------------------------------------------------ !converto to utc tempTime1 = ToUtc (time1) tempTime2 = ToUtc (time2) IF( tempTime1 % year /= tempTime2 % year .OR. & tempTime1 % month /= tempTime2 % month .OR. & tempTime1 % day /= tempTime2 % day .OR. & tempTime1 % hour /= tempTime2 % hour .OR. & tempTime1 % minute /= tempTime2 % minute .OR. & tempTime1 % second /= tempTime2 % second ) THEN isInequal = .TRUE. ELSE isInequal = .FALSE. END IF END FUNCTION Inequality !============================================================================== !| Description: ! Converts the specified string representation of a ! date and time to its `DateTime` equivalent. SUBROUTINE Parse & ! (time, string) USE StringManipulation, ONLY : & ! Imported routines: StringToShort IMPLICIT NONE ! Arguments with intent(in): CHARACTER (LEN = timeStringLength), INTENT(IN) :: string ! Arguments with intent(out): TYPE (DateTime), INTENT(OUT) :: time !------------end of declaration------------------------------------------------ !1234567890123456789012345 !2007-03-05T01:00:00+02:00 time % year = StringToShort ( string (1:4) ) time % month = StringToShort ( string (6:7) ) time % day = StringToShort ( string (9:10) ) time % hour = StringToShort ( string (12:13) ) time % minute = StringToShort ( string (15:16) ) time % second = StringToShort ( string (18:19) ) time % TZhour = StringToShort ( string (21:22) ) time % TZminute = StringToShort ( string (24:25) ) time % TZsign = string (20:20) CALL DateCheck ( time ) END SUBROUTINE Parse !============================================================================== !| Description: ! Converts the value of the current `DateTime` object to its equivalent ! string representation SUBROUTINE TimeToString & ! (string, time) USE StringManipulation, ONLY: & ! Imported routines: ToString IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time ! Arguments with intent(out): CHARACTER(LEN = timeStringLength), INTENT(OUT) :: string !------------end of declaration------------------------------------------------ string = TRIM ( ToString ( time % year, fmt = '(I4.4)' ) ) // '-' // & TRIM ( ToString ( time % month, fmt = '(I2.2)' ) ) // '-' // & TRIM ( ToString ( time % day, fmt = '(I2.2)' ) ) // 'T' // & TRIM ( ToString ( time % hour, fmt = '(I2.2)' ) ) // ':' // & TRIM ( ToString ( time % minute, fmt = '(I2.2)' ) ) // ':' // & TRIM ( ToString ( time % second, fmt = '(I2.2)' ) ) // & time % TZsign // & TRIM ( ToString ( time % TZhour, fmt = '(I2.2)' ) ) // ':' // & TRIM ( ToString ( time % TZminute, fmt = '(I2.2)' ) ) END SUBROUTINE TimeToString !============================================================================== !| Description: ! Create an exact copy of `DateTime` SUBROUTINE Copy & ! (time2, time1) IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time1 ! Arguments with intent(out): TYPE (DateTime), INTENT(OUT) :: time2 !------------end of declaration------------------------------------------------ time2 % year = time1 % year time2 % month = time1 % month time2 % day = time1 % day time2 % hour = time1 % hour time2 % minute = time1 % minute time2 % second = time1 % second time2 % TZhour = time1 % TZhour time2 % TZminute = time1 % TZminute time2 % TZsign = time1 % TZsign END SUBROUTINE Copy !============================================================================== !| Description: ! check that date do not contain errors. SUBROUTINE DateCheck & ! ( time ) IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time ! Local variables: CHARACTER (LEN = timeStringLength) :: string !------------end of declaration------------------------------------------------ IF (DateTimeIsDefault(time)) THEN RETURN !skip check END IF IF ( time % second < 0 .OR. time % second >= 60 ) THEN string = time CALL Catch ('error', 'Chronos', 'second ', & code = DateTimeError, argument = string ) END IF IF ( time % minute < 0 .OR. time % minute >= 60 ) THEN string = time CALL Catch ('error', 'Chronos', 'minute ', & code = DateTimeError, argument = string ) END IF IF ( time % hour < 0 .OR. time % hour >= 25 ) THEN string = time CALL Catch ('error', 'Chronos', 'hour ', & code = DateTimeError, argument = string ) END IF IF ( time % year <= 0) THEN string = time CALL Catch ('error', 'Chronos', 'year ', & code = DateTimeError, argument = string ) END IF IF ( time % month <= 0 .OR. time % month > 12 ) THEN string = time CALL Catch ('error', 'Chronos', 'month ', & code = DateTimeError, argument = string ) ELSE IF ( time % day <= 0 .OR. time % day > & DaysInMonth (time % month, time % year) ) THEN string = time CALL Catch ('error', 'Chronos', 'day ', & code = DateTimeError, argument = string ) END IF END SUBROUTINE DateCheck !============================================================================== !| Description: ! Returns `true` if the specified year is a leap year ! ! Method: ! ! In the _Gregorian_ calendar, a normal year consists of 365 days. ! Because the actual length of a sidereal year (the time required ! for the Earth to revolve once about the Sun) is actually 365.25635 days, ! a "leap year" of 366 days is used once every four years to eliminate the ! error caused by three normal (but short) years. Any year that is evenly ! divisible by 4 is a leap year: for example, 1988, 1992, and 1996 ! are leap years. ! However, there is still a small error that must be accounted for. ! To eliminate this error, the _Gregorian_ calendar stipulates that a year ! that is evenly divisible by 100 (for example, 1900) is a leap year only ! if it is also evenly divisible by 400. ! For this reason, the following years are not leap years: ! 1700, 1800, 1900, 2100, 2200, 2300, 2500, 2600 ! This is because they are evenly divisible by 100 but not by 400. ! The following years are leap years: ! 1600, 2000, 2400 ! This is because they are evenly divisible by both 100 and 400. FUNCTION IsLeapYear & ! (year) & ! RESULT (isLeap) IMPLICIT NONE ! Arguments with intent(in): INTEGER (KIND = short), INTENT(IN) :: year ! Local variables: LOGICAL :: isLeap !------------end of declaration------------------------------------------------ IF ( MOD ( year, 4 ) == 0 ) THEN IF ( MOD ( year, 400) > 0 .AND. MOD ( year, 100) == 0 ) THEN isLeap = .FALSE. ELSE isLeap = .TRUE. END IF ELSE isLeap = .FALSE. END IF END FUNCTION IsLeapYear !============================================================================== !| Description: ! Returns the number of days in the specified month. It accounts ! for leap years FUNCTION DaysInMonth & ! (month, year) & ! RESULT (days) IMPLICIT NONE ! Arguments with intent(in): INTEGER (KIND = short), INTENT(IN) :: year, month ! Local variables: INTEGER (KIND = short) :: days !------------end of declaration------------------------------------------------ IF( (month == 1) .OR. (month == 3) .OR. (month == 5) .OR. (month == 7) .OR. & (month == 8) .OR. (month == 10) .OR. (month == 12) ) THEN days = 31 ELSE IF ( (month == 4) .OR. (month == 6) .OR. (month == 9) .OR. & (month == 11) ) THEN days = 30 ELSE IF (month == 2) THEN IF ( IsLeapYear (year) ) THEN days = 29 ELSE days = 28 END IF END IF END FUNCTION DaysInMonth !============================================================================== !| Description: ! Gets the day of the year represented by this instance. Returns ! 366 for leap years FUNCTION DayOfYear & ! (time, leap) & ! RESULT (day) IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time CHARACTER (LEN = *), OPTIONAL, INTENT(IN) :: leap ! 'noleap' ignores 29th february of leap years ! Local variables: INTEGER (KIND = short) :: day, i TYPE (DateTime) :: february29 !------------end of declaration------------------------------------------------ day = 0 DO i = 1, time % month - 1 day = day + DaysInMonth (i, time % year) END DO day = day + time % day IF ( PRESENT (leap) ) THEN IF ( leap == 'noleap' ) THEN IF ( IsLeapYear (time % year) ) THEN !string = time !february29 = string february29 = time february29 % month = 2 february29 % day = 29 IF ( time >= february29 ) THEN day = day - 1 END IF END IF ELSE CALL Catch ('warning', 'Chronos', 'unknown option in DayOfYear: ', & code = unknownOption, argument = leap ) END IF ELSE END IF END FUNCTION DayOfYear !============================================================================== !| Description: ! Gets the second of the year represented by this instance. FUNCTION SecondOfYear & ! (time) & ! RESULT (second) USE Units, ONLY: & ! Imported parameters: day, hour, minute IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time ! Local variables: INTEGER (KIND = short) :: second !------------end of declaration------------------------------------------------ second = (DayOfYear (time) - 1) * day second = second + GetHour (time) * hour + GetMinute (time) * minute + & GetSecond (time) END FUNCTION SecondOfYear !============================================================================== !| Description: ! Gets the second to the end of the year represented by this instance. FUNCTION SecondsToEnd & ! (time) & ! RESULT (second) IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time ! Local variables: INTEGER (KIND = short) :: second !------------end of declaration------------------------------------------------ IF ( IsLeapYear (time % year) ) THEN second = 366 * 86400 - SecondOfYear (time) ELSE second = 365 * 86400 - SecondOfYear (time) END IF END FUNCTION SecondsToEnd !============================================================================== !| Description: ! Adds the specified number of seconds to the value of this instance. ! If number of seconds is a negative number, the amount is subtracted FUNCTION AddSeconds & ! (time1, step) & ! RESULT (time2) IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time1 INTEGER, INTENT(IN) :: step ! Local variables: TYPE (DateTime):: time2 INTEGER :: maxDay !------------end of declaration------------------------------------------------ time2 = time1 IF(step >= 0) THEN time2 % second = time2 % second + step IF(time2 % second >= 60) THEN time2 % minute = time2 % minute + INT ( time2 % second / 60 ) time2 % second = time2 % second - INT ( time2 % second / 60 ) * 60 IF(time2 % minute >= 60) THEN time2 % hour = time2 % hour + INT ( time2 % minute / 60) time2 % minute = time2 % minute - INT(time2%minute / 60) * 60 IF(time2 % hour >= 24) THEN time2 % day = time2 % day + INT(time2 % hour / 24) time2 % hour = time2 % hour - INT(time2%hour / 24) * 24 maxDay = DaysInMonth (time2 % month, time2 % year) DO WHILE (time2 % day > maxDay) time2 % month = time2 % month + 1 time2 % day = time2 % day - maxDay IF ( time2 % month == 13) THEN time2 % year = time2 % year + 1 time2 % month = 1 END IF maxDay = DaysInMonth (time2 % month, time2 % year) END DO END IF END IF END IF ELSE time2 % second = time2 % second + step IF(time2 % second < 0) THEN time2 % minute = time2 % minute + INT(time2 % second / 60) - 1 time2 % second = time2 % second + (-INT(time2 % second / 60) + 1) * 60 IF(time2 % second == 60) THEN time2 % second = 0 time2 % minute = time2 % minute + 1 END IF IF(time2 % minute < 0) THEN time2 % hour = time2 % hour + INT(time2 % minute / 60) - 1 time2 % minute = time2 % minute + (-INT(time2 % minute / 60) + 1) * 60 IF(time2 % minute == 60) THEN time2 % minute = 0 time2 % hour = time2 % hour + 1 END IF IF(time2 % hour < 0) THEN time2 % day = time2 % day + INT(time2 % hour / 24) - 1 time2 % hour = time2 % hour + (-INT(time2 % hour / 24) + 1) * 24 IF(time2 % hour == 24) THEN time2 % hour = 0 time2 % day = time2 % day + 1 END IF DO WHILE (time2 % day <= 0) IF(time2 % month - 1 <= 0) time2 % month = 13 maxDay = DaysInMonth(time2 % month - 1,time2 % year) time2 % month = time2 % month - 1 time2 % day = time2 % day + maxDay IF(time2%month == 12) THEN time2 % year = time2 % year - 1 END IF maxDay = DaysInMonth(time2 % month,time2 % year) END DO END IF END IF END IF END IF CALL DateCheck (time2) END FUNCTION AddSeconds !============================================================================== !| Description: ! Adds the specified number of days to the value of this instance. FUNCTION AddDays & ! (time1, step) & ! RESULT (time2) USE Units, ONLY: & ! imported parameters: day IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time1 INTEGER, INTENT(IN) :: step ! Local variables: TYPE (DateTime):: time2 !------------end of declaration------------------------------------------------ time2 = AddSeconds (time1, INT(step * day) ) END FUNCTION AddDays !============================================================================== !| Description: ! Adds the specified number of hours to the value of this instance. FUNCTION AddHours & ! (time1, step) & ! RESULT (time2) USE Units, ONLY: & ! Imported parameters: hour IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time1 INTEGER, INTENT(IN) :: step ! Local variables: TYPE (DateTime):: time2 !------------end of declaration------------------------------------------------ time2 = AddSeconds (time1, INT(step * hour) ) END FUNCTION AddHours !============================================================================== !| Description: ! Adds the specified number of minutes to the value of this instance. FUNCTION AddMinutes & ! (time1, step) & ! RESULT (time2) USE Units, ONLY: & ! Imported parameters: minute IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time1 INTEGER, INTENT(IN) :: step ! Local variables: TYPE (DateTime):: time2 !------------end of declaration------------------------------------------------ time2 = AddSeconds (time1, INT(step * minute) ) END FUNCTION AddMinutes !============================================================================== !| Description: ! calculate the difference in seconds between two date: time1 - time2 FUNCTION TimeDifference & ! (time1, time2) & RESULT (seconds) USE Units, ONLY: & ! Imported parameters: day IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time1, time2 ! Local variables: INTEGER (KIND = long) :: seconds, a, b, c INTEGER (KIND = short) :: i TYPE (DateTime) :: tempTime1, tempTime2 !------------end of declaration------------------------------------------------ !converto to utc tempTime1 = ToUtc (time1) tempTime2 = ToUtc (time2) a = SecondOfYear (tempTime1) b = SecondOfYear (tempTime2) c = 0 DO i = tempTime2 % year, tempTime1 % year - 1 IF (IsLeapYear (i)) THEN c = c + 366 * day ELSE c = c + 365 * day END IF END DO seconds = a + c - b END FUNCTION TimeDifference !============================================================================== !| Description: ! Returns true if datetime object is expressed in UTC FUNCTION IsUTC & ! (time) & ! RESULT (utc) IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time ! Local variables: LOGICAL :: utc !------------end of declaration------------------------------------------------ IF (time % TZhour == 0 .AND. time % TZminute == 0) THEN utc = .TRUE. ELSE utc = .FALSE. END IF END FUNCTION IsUTC !============================================================================== !| Description: ! Converts the value of the current `DateTime` object to ! Coordinated Universal Time (UTC). FUNCTION ToUTC & ! (time1) & ! RESULT (time2) USE Units, ONLY: & !Imported parameters hour, minute IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time1 ! Local variables: TYPE (DateTime) :: time2 INTEGER :: variation !------------end of declaration------------------------------------------------ !reset time2 time2 = timeDefault !calculate variation in seconds variation = time1 % TZhour * hour + time1 % TZminute * minute !Apply variation to convert to UTC IF (time1 % TZsign == '+' ) THEN !subtract variation time2 = AddSeconds (time1, - variation) ELSE IF (time1 % TZsign == '-' ) THEN !add variation time2 = AddSeconds (time1, variation) END IF time2 % TZhour = 0 time2 % TZminute = 0 time2 % TZsign = '+' END FUNCTION ToUTC !============================================================================== !| Description: ! Gets a `DateTime` object that is set to the current date and time on ! this computer, expressed as the local time. FUNCTION Now & ! () & ! RESULT (time) USE StringManipulation, ONLY: & !Imported routines StringToShort IMPLICIT NONE ! Local variables: TYPE (DateTime) :: time ! Local scalars: CHARACTER ( LEN = 8 ) :: systemDate CHARACTER ( LEN = 10 ) :: systemTime INTEGER (KIND = short) :: values(8) CHARACTER ( LEN = 5 ) :: zone !------------end of declaration------------------------------------------------ CALL date_and_time ( systemDate, systemTime, zone, values ) time % year = values(1) time % month = values(2) time % day = values(3) time % hour = values(5) time % minute = values(6) time % second = values(7) time % TZhour = StringToShort ( zone (2:3) ) time % TZminute = StringToShort ( zone (4:5) ) time % TZsign = zone (1:1) END FUNCTION Now !============================================================================== !| Description: ! Gets a `DateTime` object that is set to the current date and time on ! this computer, expressed as the Coordinated Universal Time (UTC). FUNCTION UtcNow & ! () & ! RESULT (time) IMPLICIT NONE ! Local variables: TYPE (DateTime) :: time !------------end of declaration------------------------------------------------ time = ToUTC ( Now() ) END FUNCTION UtcNow !============================================================================== !| Description: ! Gets the year of the datetime represented by this instance FUNCTION GetYear & ! (time) & ! RESULT (year) IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time ! Local variables: INTEGER (KIND = short) :: year !------------end of declaration------------------------------------------------ year = time % year END FUNCTION GetYear !============================================================================== !| Description: ! Gets the month of the datetime represented by this instance FUNCTION GetMonth & ! (time) & ! RESULT (month) IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time ! Local variables: INTEGER (KIND = short) :: month !------------end of declaration------------------------------------------------ month = time % month END FUNCTION GetMonth !============================================================================== !| Description: ! Gets the day of the datetime represented by this instance FUNCTION GetDay & ! (time) & ! RESULT (day) IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time ! Local variables: INTEGER (KIND = short) :: day !------------end of declaration------------------------------------------------ day = time % day END FUNCTION GetDay !============================================================================== !| Description: ! Gets the day of week (0 - 6) (Sunday to Saturday ! The formula is: ! ! `(yearCode + monthCode + centuryCode + dateNumber - leapYearCode) mod 7 ` ! ! To calculate the`yearCode`, use this formula: ! ! `(YY + (YY div 4)) mod 7` ! ! `YY` is the last two digits of the year. ! ! The `monthCode`: ! ! * January = 0 ! * February = 3 ! * March = 3 ! * April = 6 ! * May = 1 ! * June = 4 ! * July = 6 ! * August = 2 ! * September = 5 ! * October = 0 ! * November = 3 ! * December = 5 ! ! The `centuryCode` for the Gregorian Calendar: ! ! * 1700s = 4 ! * 1800s = 2 ! * 1900s = 0 ! * 2000s = 6 ! * 2100s = 4 ! * 2200s = 2 ! * 2300s = 0 ! ! `dateNumber` is the day of Month ! `leapYearCode`: if the date is in a January or February of a leap year, ! you have to subtract one from your total before the final step. ! ! References: ! ! https://artofmemory.com/blog/how-to-calculate-the-day-of-the-week/ ! FUNCTION GetDayOfWeek & ! (time) & ! RESULT (day) IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time ! Local variables: INTEGER (KIND = short) :: day !! returned value INTEGER (KIND = short) :: yearCode !! year code INTEGER (KIND = short) :: monthCode !! month code INTEGER (KIND = short) :: centuryCode !! month code INTEGER (KIND = short) :: leapYearCode !! leap year code INTEGER (KIND = short) :: dateNumber !! the day of month INTEGER (KIND = short) :: yy !! the last two digits of the year INTEGER (KIND = short) :: yyyy !! year (four digits) INTEGER (KIND = short) :: month !! month number CHARACTER (LEN = 4) :: year !------------end of declaration------------------------------------------------ !compute the yearCode (YY + (YY div 4)) mod 7 yyyy = GetYear (time) year = ToString (yyyy ) yy = StringToLong ( year (3:4) ) yearCode = MOD ( yy + INT(yy/4), 7) !set the monthCode month = GetMonth (time) SELECT CASE (month ) CASE (1,10) monthCode = 0 CASE (2,3,11) monthCode = 3 CASE (4,7) monthCode = 6 CASE (5) monthCode = 1 CASE (6) monthCode = 4 CASE (8) monthCode = 2 CASE (9,12) monthCode = 5 END SELECT !set the centuryCode SELECT CASE ( yyyy ) CASE (1700:1799) centuryCode = 4 CASE (1800:1899) centuryCode = 2 CASE (1900:1999) centuryCode = 0 CASE (2000:2099) centuryCode = 6 CASE (2100:2199) centuryCode = 4 CASE (2200:2299) centuryCode = 2 CASE (2300:2399) centuryCode = 0 END SELECT ! set leapYearCode leapYearCode = 0 IF ( IsLeapYear (yyyy) ) THEN IF ( month <= 2) THEN leapYearCode = -1 END IF END IF !dateNumber dateNumber = GetDay (time) ! yearCode + monthCode + centuryCode + dateNumber - leapYearCode) mod 7 day = MOD (yearCode + monthCode + centuryCode + dateNumber - leapYearCode, 7) RETURN END FUNCTION GetDayOfWeek !============================================================================== !| Description: ! Gets the month of the datetime represented by this instance FUNCTION GetHour & ! (time) & ! RESULT (hour) IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time ! Local variables: INTEGER (KIND = short) :: hour !------------end of declaration------------------------------------------------ hour = time % hour END FUNCTION GetHour !============================================================================== !| Description: ! Gets the minute of the datetime represented by this instance FUNCTION GetMinute & ! (time) & ! RESULT (minute) IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time ! Local variables: INTEGER (KIND = short) :: minute !------------end of declaration------------------------------------------------ minute = time % minute END FUNCTION GetMinute !============================================================================== !| Description: ! Gets the second of the datetime represented by this instance FUNCTION GetSecond & ! (time) & ! RESULT (second) IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time ! Local variables: INTEGER (KIND = short) :: second !------------end of declaration------------------------------------------------ second = time % second END FUNCTION GetSecond !============================================================================== !| Description: ! Gets the string representing time zone of the datetime ! represented by this instance. Example: '+02:00' FUNCTION GetTimeZone & ! (time) & ! RESULT (timeZone) IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time ! Local variables: CHARACTER (LEN = 6) :: timeZone CHARACTER (LEN = 25) :: string !------------end of declaration------------------------------------------------ string = time timeZone = string (20:25) END FUNCTION GetTimeZone !============================================================================== !| Description: ! Set the year of the datetime represented by this instance SUBROUTINE SetYear & ! (year, time) IMPLICIT NONE ! Arguments with intent(in): INTEGER (KIND = short), INTENT(IN) :: year ! Arguments with intent(out): TYPE (DateTime), INTENT(OUT) :: time !------------end of declaration------------------------------------------------ time % year = year CALL DateCheck (time) END SUBROUTINE SetYear !============================================================================== !| Description: ! Set the month of the datetime represented by this instance SUBROUTINE SetMonth & ! (month, time) IMPLICIT NONE ! Arguments with intent(in): INTEGER (KIND = short), INTENT(IN) :: month ! Arguments with intent(out): TYPE (DateTime), INTENT(OUT) :: time !------------end of declaration------------------------------------------------ time % month = month CALL DateCheck (time) END SUBROUTINE SetMonth !============================================================================== !| Description: ! Set the day of the datetime represented by this instance SUBROUTINE SetDay & ! (day, time) IMPLICIT NONE ! Arguments with intent(in): INTEGER (KIND = short), INTENT(IN) :: day ! Arguments with intent(out): TYPE (DateTime), INTENT(OUT) :: time !------------end of declaration------------------------------------------------ time % day = day CALL DateCheck (time) END SUBROUTINE SetDay !============================================================================== !| Description: ! Set the hour of the datetime represented by this instance SUBROUTINE SetHour & ! (hour, time) IMPLICIT NONE ! Arguments with intent(in): INTEGER (KIND = short), INTENT(IN) :: hour ! Arguments with intent(out): TYPE (DateTime), INTENT(OUT) :: time !------------end of declaration------------------------------------------------ time % hour = hour CALL DateCheck (time) END SUBROUTINE SetHour !============================================================================== !| Description: ! Set the minute of the datetime represented by this instance SUBROUTINE SetMinute & ! (minute, time) IMPLICIT NONE ! Arguments with intent(in): INTEGER (KIND = short), INTENT(IN) :: minute ! Arguments with intent(out): TYPE (DateTime), INTENT(OUT) :: time !------------end of declaration------------------------------------------------ time % minute = minute CALL DateCheck (time) END SUBROUTINE SetMinute !============================================================================== !| Description: ! Set the second of the datetime represented by this instance SUBROUTINE SetSecond & ! (second, time) IMPLICIT NONE ! Arguments with intent(in): INTEGER (KIND = short), INTENT(IN) :: second ! Arguments with intent(out): TYPE (DateTime), INTENT(OUT) :: time !------------end of declaration------------------------------------------------ time % second = second CALL DateCheck (time) END SUBROUTINE SetSecond !============================================================================== !| Description: ! Set the timezone of the datetime represented by this instance SUBROUTINE SetTimeZone & ! (timeZone, time) USE StringManipulation, ONLY : & ! Imported routines: StringCompact IMPLICIT NONE ! Arguments with intent(in): CHARACTER (LEN = *), INTENT(IN) :: timeZone ! Arguments with intent(out): TYPE (DateTime), INTENT(OUT) :: time ! Local variables: CHARACTER (LEN = 25) :: string !------------end of declaration------------------------------------------------ string = time string (20:25) = timeZone time = string CALL DateCheck (time) END SUBROUTINE SetTimeZone !============================================================================== !| Description: ! Check if datetime is set to default FUNCTION DateTimeIsDefault & ! (time) & ! RESULT (isDefault) IMPLICIT NONE ! Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time ! Local declarations: LOGICAL :: isDefault !------------end of declaration------------------------------------------------ IF (time % year == 0 .AND. & time % month == 0 .AND. & time % day == 0 .AND. & time % hour == 0 .AND. & time % minute == 0 .AND. & time % second == 0 ) THEN isDefault = .TRUE. ELSE isDefault = .FALSE. END IF RETURN END FUNCTION DateTimeIsDefault !http://msdn.microsoft.com/en-us/library/system.datetime_members.aspx !AddHours, AddMinutes, AddSeconds,AddDays,AddYears, AddMonths END MODULE Chronos